var EXPORTED_SYMBOLS = ['g_SyncPlaces'];

const Cc = Components.classes;
const Ci = Components.interfaces;

Components.utils.import("resource://gre/modules/utils.js");
Components.utils.import("resource://gsync/utils.js");

var g_SyncPlaces = {

    TYPE_FOLDER: PlacesUtils.bookmarks.TYPE_FOLDER,
    
    TYPE_BOOKMARK: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    
    TYPE_SEPARATOR: PlacesUtils.bookmarks.TYPE_SEPARATOR,
    
    insertBookmarks: function(bookmarks){
    
        for (idx in bookmarks) {
            this.createBookmark(bookmarks[idx]);
        }
    },

    isFolder: function(itemId){
    
        return this.getType(itemId) == this.TYPE_FOLDER;
    },
    
    isSeparator: function(itemId){
    
        return this.getType(itemId) == this.TYPE_BOOKMARK;
    },
    
    isBookmark: function(itemId){
    
        return this.getType(itemId) == this.TYPE_SEPARATOR;
    },
    
    isLivemark: function(itemId){
    
        return PlacesUtils.livemarks.isLivemark(itemId) || PlacesUtils.livemarks.isLivemark(this.getParent(itemId));
    },
    
    getType: function(itemId){
    
        return PlacesUtils.bookmarks.getItemType(itemId);
    },
    
    getParent: function(itemId){
    
        return PlacesUtils.bookmarks.getFolderIdForItem(itemId);
    },
    
    getUri: function(itemId){
    
        return this.getPlacesNode(itemId).uri;
    },
    
    getTitle: function(itemId){
    
        return PlacesUtils.bookmarks.getItemTitle(itemId);
    },
    
    isManaged: function(itemId){
    
    
        switch (itemId) {
            case PlacesUtils.placesRootId:
                return false;
            case PlacesUtils.bookmarksMenuFolderId:
                return false;
            case PlacesUtils.tagsFolderId:
                return false;
            case PlacesUtils.toolbarFolderId:
                return false;
            case PlacesUtils.unfiledBookmarksFolderId:
                return false;
        }
        
        var nodeType = PlacesUtils.bookmarks.getItemType(itemId);
        switch (this.getType(itemId)) {
            case this.TYPE_BOOKMARK:
                return this.getUri(itemId).indexOf("place") != 0 && !this.isLivemark(itemId);
                break;
            case this.TYPE_FOLDER:
                return !this.isLivemark(itemId);
                break;
            case this.TYPE_SEPARATOR:
                return true;
                break;
            default:
                return false;
                break;
        }
    },
    
    getLevel: function(itemId){
    
        if (itemId == PlacesUtils.placesRootId) {
            return 0;
        } else {
            return this.getLevel(this.getParent(itemId)) + 1;
        }
    },
    
    
    getFolderPath: function(folderId){
    
        switch (folderId) {
        
            case PlacesUtils.placesRootId:
                LOG.error("getFolderPath() : Illegal State");
                break;
            case PlacesUtils.bookmarksMenuFolderId:
                return "";
                break;
            case PlacesUtils.unfiledBookmarksFolderId:
                return this.getTitle(PlacesUtils.unfiledBookmarksFolderId);
            case PlacesUtils.toolbarFolderId:
                return this.getTitle(PlacesUtils.toolbarFolderId);
                break;
            default:
                var parent = this.getParent(folderId);
                
                if (parent == PlacesUtils.bookmarksMenuFolderId) {
                    return this.getTitle(folderId);
                } else {
                    return this.getFolderPath(parent) + "/" + this.getTitle(folderId);
                }
        }
    },
    
    getAllBookmarks: function(){
    
        var bookmarks = [];
        
        var visitor = function(itemId){
        
            if (this.isManaged(itemId) && !this.isFolder(itemId)) {
                var bookmark = new Bookmark();
                //TODO
                bookmark.id = "";
                bookmark.date = "";
                bookmark.description = "";
                bookmark.title = this.getTitle(itemId);
                bookmark.link = this.getUri(itemId);
                bookmark.label = this.getFolderPath(this.getParent(itemId));
                bookmarks.push(bookmark);
            }
        }
        
        this.traversePreOrder(PlacesUtils.bookmarksMenuFolderId, visitor);
        this.traversePreOrder(PlacesUtils.toolbarFolderId, visitor);
        this.traversePreOrder(PlacesUtils.unfiledBookmarksFolderId, visitor);
        
        return bookmarks;
        
    },
    
    deleteAllBookmarks: function(){
    
        //TODO Perform a backup   
        var visitor = function(itemId){
        
            if (this.isManaged(itemId)) {
                if (this.isFolder(itemId) && this.isEmptyFolder(itemId)) {
                
                    var title = "";
                    try {
                        //title = this.getTitle(itemId);
                        PlacesUtils.bookmarks.removeFolder(itemId);
                        //dump("deleted folder:" + itemId + " " + title + "\n");
                    } catch (e) {
                        dump("Failure:" + itemId + "\n");
                    }
                    
                } else {
                    if (!this.isLivemark(itemId)) {
                        PlacesUtils.bookmarks.removeItem(itemId);
                        //dump("deleted bookmark:" + itemId + " " + this.getTitle(itemId) + " " + this.isLivemark(itemId) + "\n");
                    }
                }
            }
        };
        
        this.traversePostOrder(PlacesUtils.bookmarksMenuFolderId, visitor);
        this.traversePostOrder(PlacesUtils.toolbarFolderId, visitor);
        this.traversePostOrder(PlacesUtils.unfiledBookmarksFolderId, visitor);
    },
    
    
    getRootFolderId: function(pathArray){
    
        const toolbarTitle = this.getTitle(PlacesUtils.toolbarFolderId);
        const untitledTitle = this.getTitle(PlacesUtils.unfiledBookmarksFolderId);
        
        if (pathArray.length == 0) {
            return PlacesUtils.bookmarksMenuFolderId;
        } else if (pathArray[0] == untitledTitle) {
            return PlacesUtils.unfiledBookmarksFolderId;
        } else if (pathArray[0] == toolbarTitle) {
            return PlacesUtils.toolbarFolderId;
        } else {
            return PlacesUtils.bookmarksMenuFolderId;
        }
    },
    
    getFolderId: function(pathArray){
    
        const toolbarTitle = this.getTitle(PlacesUtils.toolbarFolderId);
        const untitledTitle = this.getTitle(PlacesUtils.unfiledBookmarksFolderId);
        
        var result = this.getRootFolderId(pathArray);
        
        switch (result) {
        
            case PlacesUtils.unfiledBookmarksFolderId:
                result = PlacesUtils.unfiledBookmarksFolderId;
                break;
                
            case PlacesUtils.toolbarFolderId:
                
                result = PlacesUtils.toolbarFolderId;
                if (pathArray.length > 1) {
                    for (var i = 1; i < pathArray.length; i++) {
                        result = PlacesUtils.bookmarks.getChildFolder(result, pathArray[i]);
                        if (result == 0) {
                            break;
                        }
                    }
                }
                break;
            default:
                result = PlacesUtils.bookmarksMenuFolderId;
                if (pathArray.length > 0 && pathArray[0].length > 0) {
                    for (var i = 0; i < pathArray.length; i++) {
                        result = PlacesUtils.bookmarks.getChildFolder(result, pathArray[i]);
                        if (result == 0) {
                            break;
                        }
                    }
                }
        }
        return result;
    },
    
    createFolders: function(pathArray){
    
        var result;
        for (var i = 0; i < pathArray.length; i++) {
            result = this.getFolderId(pathArray.slice(0, i + 1));
            if (result == 0) {
                var folderId = i == 0 ? this.getRootFolderId(pathArray) : this.getFolderId(pathArray.slice(0, i));
                result = PlacesUtils.bookmarks.createFolder(folderId, pathArray[i], -1);
            }
        }
        return result;
    },
    
    createBookmark: function(bookmark){
    
        //TODO Divider	  
        
        LOG.trace("g_SyncPlaces.createBookmark() - " + bookmark);
        
        var path = bookmark.label.split("/");
        
        var folderId = this.getFolderId(path);
        if (folderId == 0) {
            folderId = this.createFolders(path);
        }
        
        //TODO FIX for Javascript urls's        
        try {
            var urlService = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
            var fixedUri = urlService.createFixupURI(bookmark.link, 0);
            PlacesUtils.bookmarks.insertBookmark(folderId, fixedUri, -1, bookmark.title || "");
        } catch (e) {
            LOG.error("Error:" + bookmark + "\n", e);
        }
        
        //TODO description
        if (bookmark.description.length > 0) {
        
        }
    },
    
    dumpTree: function(rootFolderId){
    
        var visitor = function(nodeId){
        
            var level = this.getLevel(nodeId);
            var managed = this.isManaged(nodeId) ? "M" : "U";
            var levelStr = ">";
            for (var i = 0; i < level; i++) {
                levelStr += ">";
            }
            
            var uri = this.getUri(nodeId);
            dump(managed + " " + levelStr + " " + nodeId + " " + PlacesUtils.bookmarks.getItemTitle(nodeId) + " (" + uri + ")\n");
        }
        
        this.traversePostOrder(rootFolderId, visitor);
    },
    
    
    getPlacesNode: function(itemId){
    
    
        if (itemId == PlacesUtils.placesRootId) {
            return {
                uri: "PLACES_ROOT"
            };
        }
        
        var result = {
            uri: "UNKNOWN"
        };
        
        
        var parentId = PlacesUtils.bookmarks.getFolderIdForItem(itemId);
        
        return this.executeQuery(parentId, function(root){
            for (var i = 0; i < root.childCount; i++) {
                var node = root.getChild(i);
                if (node.itemId == itemId) {
                    return node;
                }
            }
        });
    },
    
    isEmptyFolder: function(folderId){
    
        return this.executeQuery(folderId, function(root){
            return root.childCount == 0;
        });
    },
    
    executeQuery: function(folderId, closure){
    
        var options = PlacesUtils.history.getNewQueryOptions();
        var query = PlacesUtils.history.getNewQuery();
        query.setFolders([folderId], 1);
        var result = PlacesUtils.history.executeQuery(query, options);
        var rootNode = result.root;
        rootNode.containerOpen = true;
        var result = closure.call(this, rootNode);
        rootNode.containerOpen = false;
        return result;
    },
    
    /**
     * Preorder tree traversal
     * The tree:
     *
     *          a
     *          |
     *       b --- c
     *       |
     *    d --- e
     *
     * will be traversed in the following order: [a,b,d,e,c]
     *
     * @param {int} folderId
     * @param {Function} callback
     */
    traversePreOrder: function(folderId, callback){
    
        callback.call(this, folderId);
        this.executeQuery(folderId, function(rootNode){
            for (var i = 0; i < rootNode.childCount; i++) {
                var node = rootNode.getChild(i);
                var nodeType = PlacesUtils.bookmarks.getItemType(node.itemId);
                switch (nodeType) {
                    case PlacesUtils.bookmarks.TYPE_FOLDER:
                        this.traversePreOrder(node.itemId, callback);
                        break;
                    default:
                        callback.call(this, node.itemId);
                }
            }
        });
    },
    
    
    /**
     * Postorder tree traversal
     * The tree:
     *
     *          a
     *          |
     *       b --- c
     *       |
     *    d --- e
     *
     * will be traversed in the following order: [d,e,b,c,a]
     *
     * @param {Object} folderId
     * @param {Function} callback
     */
    traversePostOrder: function(folderId, callback){
    
        this.executeQuery(folderId, function(rootNode){
            for (var i = rootNode.childCount - 1; i >= 0; i--) {
                var node = rootNode.getChild(i);
                var nodeType = PlacesUtils.bookmarks.getItemType(node.itemId);
                switch (nodeType) {
                    case PlacesUtils.bookmarks.TYPE_FOLDER:
                        this.traversePostOrder(node.itemId, callback);
                        break;
                    default:
                        callback.call(this, node.itemId);
                }
            }
        });
        callback.call(this, folderId);
    },
}
